package titt.service;

import java.util.HashMap;
import java.util.Map;

import titt.head.request.MethodHead;
import titt.service.invoke.MethodInvoker;
import titt.service.invoke.MethodParser;
import titt.service.invoke.ReflectionInvoker;
import titt.service.invoke.ThreadSafeInvoker;
import titt.service.os.ServiceObjectServer;
import titt.service.proxy.server.ServerProxy;
import titt.service.strategy.TITTStrategy;
import titt.service.threading.MultipleTITTThreader;
import titt.type.TypedObject;

public class ServiceMechanics {
	
	private final TITTStrategy strat;
	private final TITTInternal internal = new TITTInternal();
	private final Map<Object, String> idMap = new HashMap<Object, String>();

	public ServiceMechanics(TITTStrategy strat) {
		this.strat = strat;
	}
	
	public ServiceObjectServer register(Object o, MethodParser mp, String[] tokens) throws Exception {
		return register(o, mp, null, tokens);
	}
	
	public ServiceObjectServer register(Object o, MethodParser mp, String link, String[] tokens) throws Exception {
		unregister(o, false);

		String serviceID;

		if(link == null) {
			serviceID = ServerProxy.get().register(o, tokens);
			ServerProxy.get().detectErrors(serviceID);
		}
		else
			serviceID = ServerProxy.get().registerWithLink(o, link, tokens);
		
		MethodInvoker inv = new ThreadSafeInvoker(o, new ReflectionInvoker(o));

		synchronized (this) {			
			idMap.put(o, serviceID);
		}

		return new ServiceObjectServer(o, inv, mp, serviceID);
	}
	
	public void unregister(Object o, boolean lazy) throws Exception {
		final Object fo = o;
		if(lazy)
			MultipleTITTThreader.get().execute(new Runnable() {
				@Override
				public void run() {
					try {
						doUnregister(fo, true);
					}
					catch(Exception e) {}
				}
			});
		else
			doUnregister(fo, false);
	}
	
	public Runnable futureHandlePackage(final ServiceObjectServer sos, final ServicePackage pack) {
		return new Runnable() {			
			@Override
			public void run() {
				try {
					handlePackage(sos, pack);
				}
				catch(Exception e) {}
			}
		};
	}
	
	public void handlePackage(ServiceObjectServer sos, ServicePackage pack) throws Exception {
		String packageID = pack.getPackageID();
		MethodHead m = sos.getMethodParser().parse(pack.getHead());

		if(m.getMethodName() == null) {
			close(pack);
			ServerProxy.get().sync(418, packageID);
			return;
		}

		TypedObject titt = internal.call(sos.getObject(), m.getMethodName());

		if(titt != null) {
			close(pack);
			ServerProxy.get().sync(200, titt, packageID);
			return;
		}

		strat.invokeAndRespond(m, sos, pack);
	}
	
	private void close(ServicePackage pack) throws Exception {
		if(!pack.getTITTSocket().isClosed())
			pack.getTITTSocket().close();
	}
	
	private void doUnregister(Object o, boolean lazy) throws Exception {
		String serviceID;
		
		synchronized (this) {			
			serviceID = idMap.remove(o);
		}
		
		if(serviceID != null)
			ServerProxy.get().unregister(serviceID, lazy);
		
		synchronized (this) {			
			if(idMap.size() == 0)
				MultipleTITTThreader.get().clear();	
		}
	}
	
}
